package com.stox.client.ui.javafx.chartingy.model.axis;

import java.util.Calendar;
import java.util.List;

import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Region;

import com.stox.client.ui.javafx.chartingy.model.Rectangle;
import com.stox.client.ui.javafx.chartingy.model.chart.PrimaryChart;
import com.stox.client.ui.javafx.chartingy.model.enumeration.BarType;
import com.stox.client.ui.javafx.chartingy.model.plot.Data;
import com.stox.client.ui.javafx.core.util.CSS;
import com.stox.client.ui.javafx.core.util.UI;
import com.stox.shared.core.model.Bar;
import com.stox.shared.core.model.Scrip;
import com.stox.shared.core.util.Constant;
import com.sun.javafx.tk.Toolkit;

public class DateAxis extends Region implements EventHandler<MouseEvent>{
	private static final double tickHeight = 5;

	private final PrimaryChart primaryChart;
	private int upperBoundIndex = 0;
	private int lowerBoundIndex = 0;
	private final Label valueLabel = UI.style(new Label(), CSS.valueLabel);

	public DateAxis(final PrimaryChart chart) {
		primaryChart = chart;
		UI.style(this, CSS.undecorated, CSS.glass, CSS.dateAxis);
		getChildren().add(valueLabel);
		valueLabel.setLayoutY(0);
		valueLabel.setVisible(false);
		chart.getChartingView().getSplitPane().addEventFilter(MouseEvent.ANY, this);
		chart.getChartContentArea().widthProperty().addListener(new ChangeListener<Number>() {
			@Override
			public void changed(final ObservableValue<? extends Number> observable, final Number oldValue, final Number newValue) {
				setWidth(newValue.doubleValue());
			}
		});
	}
	
	@Override
	public void handle(MouseEvent event) {
		if(MouseEvent.MOUSE_ENTERED.equals(event.getEventType())){
			valueLabel.setVisible(true);
		}else if(MouseEvent.MOUSE_EXITED.equals(event.getEventType())){
			valueLabel.setVisible(false);
		}else if(MouseEvent.MOUSE_MOVED.equals(event.getEventType()) || MouseEvent.MOUSE_DRAGGED.equals(event.getEventType())){
			valueLabel.setVisible(true);
			final int index = getIndexForDisplay(event.getX());
			final List<Data<Scrip, Bar>> datas = primaryChart.getPrimaryPlot().getData();
			if ((0 <= index) && (index < datas.size())) {
				valueLabel.setText(Constant.dateFormat.format(datas.get(index).getDate()));
				valueLabel.setLayoutX(event.getX() - valueLabel.getWidth()/2);
				valueLabel.toFront();
			}
		}
	}

	public void clear() {
		primaryChart.getGrid().clearVerticalLines();
		getChildren().clear();
		getChildren().add(valueLabel);
	}

	public void addLabel(final String text, final double x, final boolean addGridLine) {
		final Label label = new Label(text);
		final double width = Toolkit.getToolkit().getFontLoader().computeStringWidth(text, label.getFont());
		double left = x - (width / 2) - label.getInsets().getLeft();
		double right = x + width/2 + label.getInsets().getRight();
		for(Node node : getChildren()){
			if(node instanceof Label){
				Label labelNode = (Label)node;
				double minX = node.getLayoutX();
				double maxX = minX + labelNode.getInsets().getLeft() + Toolkit.getToolkit().getFontLoader().computeStringWidth(labelNode.getText(), label.getFont()) + labelNode.getInsets().getRight();
				if((left > minX && left < maxX) ||
						(right > minX && right < maxX)){
					
					return;
				}
			}
		}
		if(addGridLine){
			primaryChart.getGrid().addVerticalLine(x);
		}
		label.setLayoutX(left);
		label.setLayoutY(tickHeight + 3);
		getChildren().add(label);
		valueLabel.toFront();
	}

	@Override
	public void resize(final double width, final double height) {
		setHeight(height);
	}

	public PrimaryChart getPrimaryChart() {
		return primaryChart;
	}

	public double getUnitWidth() {
		return getWidth() / ((lowerBoundIndex - upperBoundIndex) + 1);
	}

	public double getDisplayPosition(final int index) {
		if (BarType.EQUIVOLUME.equals(primaryChart.getChartingView().getBarType())) {

			return 0;
		} else {
			final double unitWidth = getUnitWidth();
			return (unitWidth / 2) + ((lowerBoundIndex - index) * unitWidth);
		}
	}

	public double getDisplayPosition(final long date) {
		final int index = getIndexForValue(date);
		final List<Data<Scrip, Bar>> datas = primaryChart.getPrimaryPlot().getData();
		if ((0 <= index) && (index < datas.size())) {
			return getDisplayPosition(index);
		}
		return -1;
	}

	public long getValueForDisplay(final double position) {
		final int index = getIndexForDisplay(position);
		final List<Data<Scrip, Bar>> datas = primaryChart.getPrimaryPlot().getData();
		if ((0 <= index) && (index < datas.size())) {
			final long date = datas.get(index).getDate();
			return date;
		}
		return -1;
	}

	public int getIndexForDisplay(final double position) {
		if (BarType.EQUIVOLUME.equals(primaryChart.getChartingView().getBarType())) {
			final List<Data<Scrip, Bar>> datas = primaryChart.getPrimaryPlot().getData();
			for (int index = lowerBoundIndex; index >= upperBoundIndex; index--) {
				final Data<Scrip, Bar> data = datas.get(index);
				final Rectangle rectangle = data.getDataNode().getRectangle();
				if ((position >= rectangle.getX()) && (position < (rectangle.getX() + rectangle.getWidth()))) {
					return index;
				}
			}
		}
		final double unitWidth = getUnitWidth();
		return (int) (lowerBoundIndex - ((position - unitWidth) / unitWidth));
	}

	public int getIndexForValue(final long date) {
		final List<Data<Scrip, Bar>> datas = primaryChart.getPrimaryPlot().getData();
		final long upperBoundDate = datas.get(upperBoundIndex).getExtraValue().getDate().getTime();
		final Calendar calendar = Calendar.getInstance();
		final long now = calendar.getTimeInMillis();
		primaryChart.getChartingView().getBarSpan().add(calendar, -1);
		final long oneOff = calendar.getTimeInMillis();
		final long diff = now - oneOff;

		for (int index = (int) (upperBoundIndex + ((upperBoundDate - date) / diff)); index >= 0; index--) {
			if ((index < (datas.size() - 1)) && (index >= 0)) {
				final Data<Scrip, Bar> data = datas.get(index);
				final Data<Scrip, Bar> previous = datas.get(index + 1);
				if ((date <= data.getDate()) && (date > previous.getDate())) {
					return index;
				}
			}
		}

		return -1;
	}

	public int getUpperBoundIndex() {
		return upperBoundIndex;
	}

	public void updateBoundIndices(final int upperBoundIndex, final int lowerBoundIndex) {
		this.upperBoundIndex = upperBoundIndex;
		this.lowerBoundIndex = lowerBoundIndex;
	}

	public int getLowerBoundIndex() {
		return lowerBoundIndex;
	}

}
